/*
 * Copyright (C) 2006 The Android Open Source Project
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cn.yunzhisheng.vui.assistant.sms;

import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;

/**
 * Contains phone state and service related information.
 * 
 * The following phone information is included in returned ServiceState:
 * 
 * <ul>
 * <li>Service state: IN_SERVICE, OUT_OF_SERVICE, EMERGENCY_ONLY, POWER_OFF
 * <li>Roaming indicator
 * <li>Operator name, short name and numeric id
 * <li>Network selection mode
 * </ul>
 */
public class ServiceState implements Parcelable {

	static final String LOG_TAG = "PHONE";

	/**
	 * Normal operation condition, the phone is registered
	 * with an operator either in home network or in roaming.
	 */
	public static final int STATE_IN_SERVICE = 0;

	/**
	 * Phone is not registered with any operator, the phone
	 * can be currently searching a new operator to register to, or not
	 * searching to registration at all, or registration is denied, or radio
	 * signal is not available.
	 */
	public static final int STATE_OUT_OF_SERVICE = 1;

	/**
	 * The phone is registered and locked. Only emergency numbers are allowed.
	 * {@more}
	 */
	public static final int STATE_EMERGENCY_ONLY = 2;

	/**
	 * Radio of telephony is explictly powered off.
	 */
	public static final int STATE_POWER_OFF = 3;

	/**
	 * Available radio technologies for GSM, UMTS and CDMA.
	 */
	/** @hide */
	public static final int RADIO_TECHNOLOGY_UNKNOWN = 0;
	/** @hide */
	public static final int RADIO_TECHNOLOGY_GPRS = 1;
	/** @hide */
	public static final int RADIO_TECHNOLOGY_EDGE = 2;
	/** @hide */
	public static final int RADIO_TECHNOLOGY_UMTS = 3;
	/** @hide */
	public static final int RADIO_TECHNOLOGY_IS95A = 4;
	/** @hide */
	public static final int RADIO_TECHNOLOGY_IS95B = 5;
	/** @hide */
	public static final int RADIO_TECHNOLOGY_1xRTT = 6;
	/** @hide */
	public static final int RADIO_TECHNOLOGY_EVDO_0 = 7;
	/** @hide */
	public static final int RADIO_TECHNOLOGY_EVDO_A = 8;
	/** @hide */
	public static final int RADIO_TECHNOLOGY_HSDPA = 9;
	/** @hide */
	public static final int RADIO_TECHNOLOGY_HSUPA = 10;
	/** @hide */
	public static final int RADIO_TECHNOLOGY_HSPA = 11;

	/**
	 * Available registration states for GSM, UMTS and CDMA.
	 */
	/** @hide */
	public static final int REGISTRATION_STATE_NOT_REGISTERED_AND_NOT_SEARCHING = 0;
	/** @hide */
	public static final int REGISTRATION_STATE_HOME_NETWORK = 1;
	/** @hide */
	public static final int REGISTRATION_STATE_NOT_REGISTERED_AND_SEARCHING = 2;
	/** @hide */
	public static final int REGISTRATION_STATE_REGISTRATION_DENIED = 3;
	/** @hide */
	public static final int REGISTRATION_STATE_UNKNOWN = 4;
	/** @hide */
	public static final int REGISTRATION_STATE_ROAMING = 5;

	private int mState = STATE_OUT_OF_SERVICE;
	private boolean mRoaming;
	private String mOperatorAlphaLong;
	private String mOperatorAlphaShort;
	private String mOperatorNumeric;
	private boolean mIsManualNetworkSelection;

	// ***** CDMA
	private int mRadioTechnology;
	private boolean mCssIndicator;
	private int mNetworkId;
	private int mSystemId;
	private int mCdmaRoamingIndicator;
	private int mCdmaDefaultRoamingIndicator;
	private int mCdmaEriIconIndex;
	private int mCdmaEriIconMode;

	/**
	 * Create a new ServiceState from a intent notifier Bundle
	 * 
	 * This method is used by PhoneStateIntentReceiver and maybe by
	 * external applications.
	 * 
	 * @param m Bundle from intent notifier
	 * @return newly created ServiceState
	 * @hide
	 */
	public static ServiceState newFromBundle(Bundle m) {
		ServiceState ret;
		ret = new ServiceState();
		ret.setFromNotifierBundle(m);
		return ret;
	}

	/**
	 * Empty constructor
	 */
	public ServiceState() {
	}

	/**
	 * Copy constructors
	 * 
	 * @param s Source service state
	 */
	public ServiceState(ServiceState s) {
		copyFrom(s);
	}

	protected void copyFrom(ServiceState s) {
		mState = s.mState;
		mRoaming = s.mRoaming;
		mOperatorAlphaLong = s.mOperatorAlphaLong;
		mOperatorAlphaShort = s.mOperatorAlphaShort;
		mOperatorNumeric = s.mOperatorNumeric;
		mIsManualNetworkSelection = s.mIsManualNetworkSelection;
		mRadioTechnology = s.mRadioTechnology;
		mCssIndicator = s.mCssIndicator;
		mNetworkId = s.mNetworkId;
		mSystemId = s.mSystemId;
		mCdmaRoamingIndicator = s.mCdmaRoamingIndicator;
		mCdmaDefaultRoamingIndicator = s.mCdmaDefaultRoamingIndicator;
		mCdmaEriIconIndex = s.mCdmaEriIconIndex;
		mCdmaEriIconMode = s.mCdmaEriIconMode;
	}

	/**
	 * Construct a ServiceState object from the given parcel.
	 */
	public ServiceState(Parcel in) {
		mState = in.readInt();
		mRoaming = in.readInt() != 0;
		mOperatorAlphaLong = in.readString();
		mOperatorAlphaShort = in.readString();
		mOperatorNumeric = in.readString();
		mIsManualNetworkSelection = in.readInt() != 0;
		mRadioTechnology = in.readInt();
		mCssIndicator = (in.readInt() != 0);
		mNetworkId = in.readInt();
		mSystemId = in.readInt();
		mCdmaRoamingIndicator = in.readInt();
		mCdmaDefaultRoamingIndicator = in.readInt();
		mCdmaEriIconIndex = in.readInt();
		mCdmaEriIconMode = in.readInt();
	}

	public void writeToParcel(Parcel out, int flags) {
		out.writeInt(mState);
		out.writeInt(mRoaming ? 1 : 0);
		out.writeString(mOperatorAlphaLong);
		out.writeString(mOperatorAlphaShort);
		out.writeString(mOperatorNumeric);
		out.writeInt(mIsManualNetworkSelection ? 1 : 0);
		out.writeInt(mRadioTechnology);
		out.writeInt(mCssIndicator ? 1 : 0);
		out.writeInt(mNetworkId);
		out.writeInt(mSystemId);
		out.writeInt(mCdmaRoamingIndicator);
		out.writeInt(mCdmaDefaultRoamingIndicator);
		out.writeInt(mCdmaEriIconIndex);
		out.writeInt(mCdmaEriIconMode);
	}

	public int describeContents() {
		return 0;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static final Parcelable.Creator<ServiceState> CREATOR = new Parcelable.Creator() {
		public ServiceState createFromParcel(Parcel in) {
			return new ServiceState(in);
		}

		public ServiceState[] newArray(int size) {
			return new ServiceState[size];
		}
	};

	/**
	 * Get current servcie state of phone
	 * 
	 * @see #STATE_IN_SERVICE
	 * @see #STATE_OUT_OF_SERVICE
	 * @see #STATE_EMERGENCY_ONLY
	 * @see #STATE_POWER_OFF
	 */
	public int getState() {
		return mState;
	}

	/**
	 * Get current roaming indicator of phone
	 * (note: not just decoding from TS 27.007 7.2)
	 * 
	 * @return true if TS 27.007 7.2 roaming is true
	 * and ONS is different from SPN
	 * 
	 */
	public boolean getRoaming() {
		return mRoaming;
	}

	/**
	 * @hide
	 */
	public int getCdmaRoamingIndicator() {
		return this.mCdmaRoamingIndicator;
	}

	/**
	 * @hide
	 */
	public int getCdmaDefaultRoamingIndicator() {
		return this.mCdmaDefaultRoamingIndicator;
	}

	/**
	 * @hide
	 */
	public int getCdmaEriIconIndex() {
		return this.mCdmaEriIconIndex;
	}

	/**
	 * @hide
	 */
	public int getCdmaEriIconMode() {
		return this.mCdmaEriIconMode;
	}

	/**
	 * Get current registered operator name in long alphanumeric format
	 * 
	 * In GSM/UMTS, long format can be upto 16 characters long
	 * In CDMA, returns the ERI text, if set, otherwise the ONS
	 * 
	 * @return long name of operator, null if unregistered or unknown
	 */
	public String getOperatorAlphaLong() {
		return mOperatorAlphaLong;
	}

	/**
	 * Get current registered operator name in short lphanumeric format
	 * 
	 * In GSM/UMST, short format can be upto 8 characters long
	 * 
	 * @return short name of operator, null if unregistered or unknown
	 */
	public String getOperatorAlphaShort() {
		return mOperatorAlphaShort;
	}

	/**
	 * Get current registered operator numeric id
	 * 
	 * In GSM/UMTS, numeric format is 3 digit country code plus 2 or 3 digit
	 * network code
	 * 
	 * The country code can be decoded using MccTable.countryCodeForMcc()
	 * 
	 * @return numeric format of operator, null if unregistered or unknown
	 */
	public String getOperatorNumeric() {
		return mOperatorNumeric;
	}

	/**
	 * Get current network selection mode
	 * 
	 * @return true if manual mode, false if automatic mode
	 */
	public boolean getIsManualSelection() {
		return mIsManualNetworkSelection;
	}

	@Override
	public int hashCode() {
		return ((mState * 0x1234)
				+ (mRoaming ? 1 : 0)
				+ (mIsManualNetworkSelection ? 1 : 0)
				+ ((null == mOperatorAlphaLong) ? 0 : mOperatorAlphaLong
					.hashCode())
				+ ((null == mOperatorAlphaShort) ? 0 : mOperatorAlphaShort
					.hashCode())
				+ ((null == mOperatorNumeric) ? 0 : mOperatorNumeric.hashCode())
				+ mCdmaRoamingIndicator + mCdmaDefaultRoamingIndicator);
	}

	@Override
	public boolean equals(Object o) {
		ServiceState s;

		try {
			s = (ServiceState) o;
		} catch (ClassCastException ex) {
			return false;
		}

		if (o == null) {
			return false;
		}

		return (mState == s.mState
				&& mRoaming == s.mRoaming
				&& mIsManualNetworkSelection == s.mIsManualNetworkSelection
				&& equalsHandlesNulls(mOperatorAlphaLong, s.mOperatorAlphaLong)
				&& equalsHandlesNulls(
					mOperatorAlphaShort,
					s.mOperatorAlphaShort)
				&& equalsHandlesNulls(mOperatorNumeric, s.mOperatorNumeric)
				&& equalsHandlesNulls(mRadioTechnology, s.mRadioTechnology)
				&& equalsHandlesNulls(mCssIndicator, s.mCssIndicator)
				&& equalsHandlesNulls(mNetworkId, s.mNetworkId)
				&& equalsHandlesNulls(mSystemId, s.mSystemId)
				&& equalsHandlesNulls(
					mCdmaRoamingIndicator,
					s.mCdmaRoamingIndicator) && equalsHandlesNulls(
			mCdmaDefaultRoamingIndicator,
			s.mCdmaDefaultRoamingIndicator));
	}

	@Override
	public String toString() {
		String radioTechnology = new String("Error in radioTechnology");

		switch (this.mRadioTechnology) {
		case 0:
			radioTechnology = "Unknown";
			break;
		case 1:
			radioTechnology = "GPRS";
			break;
		case 2:
			radioTechnology = "EDGE";
			break;
		case 3:
			radioTechnology = "UMTS";
			break;
		case 4:
			radioTechnology = "IS95A";
			break;
		case 5:
			radioTechnology = "IS95B";
			break;
		case 6:
			radioTechnology = "1xRTT";
			break;
		case 7:
			radioTechnology = "EvDo rev. 0";
			break;
		case 8:
			radioTechnology = "EvDo rev. A";
			break;
		case 9:
			radioTechnology = "HSDPA";
			break;
		case 10:
			radioTechnology = "HSUPA";
			break;
		case 11:
			radioTechnology = "HSPA";
			break;
		default:
			Log.w(LOG_TAG, "mRadioTechnology variable out of range.");
			break;
		}

		return (mState + " " + (mRoaming ? "roaming" : "home") + " "
				+ mOperatorAlphaLong + " " + mOperatorAlphaShort + " "
				+ mOperatorNumeric + " "
				+ (mIsManualNetworkSelection ? "(manual)" : "") + " "
				+ radioTechnology + " "
				+ (mCssIndicator ? "CSS supported" : "CSS not supported") + " "
				+ mNetworkId + " " + mSystemId + "RoamInd: "
				+ mCdmaRoamingIndicator + "DefRoamInd: " + mCdmaDefaultRoamingIndicator);
	}

	public void setStateOutOfService() {
		mState = STATE_OUT_OF_SERVICE;
		mRoaming = false;
		mOperatorAlphaLong = null;
		mOperatorAlphaShort = null;
		mOperatorNumeric = null;
		mIsManualNetworkSelection = false;
		mRadioTechnology = 0;
		mCssIndicator = false;
		mNetworkId = -1;
		mSystemId = -1;
		mCdmaRoamingIndicator = -1;
		mCdmaDefaultRoamingIndicator = -1;
		mCdmaEriIconIndex = -1;
		mCdmaEriIconMode = -1;
	}

	// TODO - can't this be combined with the above func..
	public void setStateOff() {
		mState = STATE_POWER_OFF;
		mRoaming = false;
		mOperatorAlphaLong = null;
		mOperatorAlphaShort = null;
		mOperatorNumeric = null;
		mIsManualNetworkSelection = false;
		mRadioTechnology = 0;
		mCssIndicator = false;
		mNetworkId = -1;
		mSystemId = -1;
		mCdmaRoamingIndicator = -1;
		mCdmaDefaultRoamingIndicator = -1;
		mCdmaEriIconIndex = -1;
		mCdmaEriIconMode = -1;
	}

	public void setState(int state) {
		mState = state;
	}

	public void setRoaming(boolean roaming) {
		mRoaming = roaming;
	}

	/**
	 * @hide
	 */
	public void setCdmaRoamingIndicator(int roaming) {
		this.mCdmaRoamingIndicator = roaming;
	}

	/**
	 * @hide
	 */
	public void setCdmaDefaultRoamingIndicator(int roaming) {
		this.mCdmaDefaultRoamingIndicator = roaming;
	}

	/**
	 * @hide
	 */
	public void setCdmaEriIconIndex(int index) {
		this.mCdmaEriIconIndex = index;
	}

	/**
	 * @hide
	 */
	public void setCdmaEriIconMode(int mode) {
		this.mCdmaEriIconMode = mode;
	}

	public void setOperatorName(String longName, String shortName,
								String numeric) {
		mOperatorAlphaLong = longName;
		mOperatorAlphaShort = shortName;
		mOperatorNumeric = numeric;
	}

	/**
	 * In CDMA mOperatorAlphaLong can be set from the ERI
	 * text, this is done from the CDMAPhone and not from the
	 * CdmaServiceStateTracker
	 * 
	 * @hide
	 */
	public void setCdmaEriText(String longName) {
		mOperatorAlphaLong = longName;
	}

	public void setIsManualSelection(boolean isManual) {
		mIsManualNetworkSelection = isManual;
	}

	/**
	 * Test whether two objects hold the same data values or both are null
	 * 
	 * @param a first obj
	 * @param b second obj
	 * @return true if two objects equal or both are null
	 */
	private static boolean equalsHandlesNulls(Object a, Object b) {
		return (a == null) ? (b == null) : a.equals(b);
	}

	/**
	 * Set ServiceState based on intent notifier map
	 * 
	 * @param m intent notifier map
	 * @hide
	 */
	private void setFromNotifierBundle(Bundle m) {
		mState = m.getInt("state");
		mRoaming = m.getBoolean("roaming");
		mOperatorAlphaLong = m.getString("operator-alpha-long");
		mOperatorAlphaShort = m.getString("operator-alpha-short");
		mOperatorNumeric = m.getString("operator-numeric");
		mIsManualNetworkSelection = m.getBoolean("manual");
		mRadioTechnology = m.getInt("radioTechnology");
		mCssIndicator = m.getBoolean("cssIndicator");
		mNetworkId = m.getInt("networkId");
		mSystemId = m.getInt("systemId");
		mCdmaRoamingIndicator = m.getInt("cdmaRoamingIndicator");
		mCdmaDefaultRoamingIndicator = m.getInt("cdmaDefaultRoamingIndicator");
	}

	/**
	 * Set intent notifier Bundle based on service state
	 * 
	 * @param m intent notifier Bundle
	 * @hide
	 */
	public void fillInNotifierBundle(Bundle m) {
		m.putInt("state", mState);
		m.putBoolean("roaming", Boolean.valueOf(mRoaming));
		m.putString("operator-alpha-long", mOperatorAlphaLong);
		m.putString("operator-alpha-short", mOperatorAlphaShort);
		m.putString("operator-numeric", mOperatorNumeric);
		m.putBoolean("manual", Boolean.valueOf(mIsManualNetworkSelection));
		m.putInt("radioTechnology", mRadioTechnology);
		m.putBoolean("cssIndicator", mCssIndicator);
		m.putInt("networkId", mNetworkId);
		m.putInt("systemId", mSystemId);
		m.putInt("cdmaRoamingIndicator", mCdmaRoamingIndicator);
		m.putInt("cdmaDefaultRoamingIndicator", mCdmaDefaultRoamingIndicator);
	}

	// ***** CDMA
	/** @hide */
	public void setRadioTechnology(int state) {
		this.mRadioTechnology = state;
	}

	/** @hide */
	public void setCssIndicator(int css) {
		this.mCssIndicator = (css != 0);
	}

	/** @hide */
	public void setSystemAndNetworkId(int systemId, int networkId) {
		this.mSystemId = systemId;
		this.mNetworkId = networkId;
	}

	/** @hide */
	public int getRadioTechnology() {
		return this.mRadioTechnology;
	}

	/** @hide */
	public int getCssIndicator() {
		return this.mCssIndicator ? 1 : 0;
	}

	/** @hide */
	public int getNetworkId() {
		return this.mNetworkId;
	}

	/** @hide */
	public int getSystemId() {
		return this.mSystemId;
	}
}
